"""Support for stack-frames.

Provides Implements a nearly complete wrapper for a stack frame.
"""
import sys
from .util import _wrap, RaiseNotImpl
import expressions, gateways, axdebug, winerror
import pythoncom
from win32com.server.exception import COMException

from .util import trace
#def trace(*args):
#       pass

class EnumDebugStackFrames(gateways.EnumDebugStackFrames):
    """A class that given a debugger object, can return an enumerator
    of DebugStackFrame objects.
    """
    def __init__(self, debugger):
        infos = []
        frame = debugger.currentframe
#               print "Stack check"
        while frame:
#                       print " Checking frame", frame.f_code.co_filename, frame.f_lineno-1, frame.f_trace,
            # Get a DebugCodeContext for the stack frame.  If we fail, then it
            # is not debuggable, and therefore not worth displaying.
            cc = debugger.codeContainerProvider.FromFileName(frame.f_code.co_filename)
            if cc is not None:
                try:
                    address = frame.f_locals['__axstack_address__']
                except KeyError:
#                                       print "Couldnt find stack address for",frame.f_code.co_filename, frame.f_lineno-1
                    # Use this one, even tho it is wrong :-(
                    address = axdebug.GetStackAddress()
                frameInfo = DebugStackFrame(frame, frame.f_lineno-1, cc), address, address+1, 0, None
                infos.append(frameInfo)
#                               print "- Kept!"
#                       else:
#                               print "- rejected"
            frame = frame.f_back

        gateways.EnumDebugStackFrames.__init__(self, infos, 0)
#       def __del__(self):
#               print "EnumDebugStackFrames dieing"

    def Next(self, count):
        return gateways.EnumDebugStackFrames.Next(self, count)

#       def _query_interface_(self, iid):
#               from win32com.util import IIDToInterfaceName
#               print "EnumDebugStackFrames QI with %s (%s)" % (IIDToInterfaceName(iid), str(iid))
#               return 0
    def _wrap(self, obj):
        # This enum returns a tuple, with 2 com objects in it.
        obFrame, min, lim, fFinal, obFinal = obj
        obFrame = _wrap(obFrame, axdebug.IID_IDebugStackFrame)
        if obFinal:
            obFinal = _wrap(obFinal, pythoncom.IID_IUnknown)
        return obFrame, min, lim, fFinal, obFinal

class DebugStackFrame(gateways.DebugStackFrame):
    def __init__(self, frame, lineno, codeContainer):
        self.frame = frame
        self.lineno = lineno
        self.codeContainer = codeContainer
        self.expressionContext = None
#       def __del__(self):
#               print "DSF dieing"
    def _query_interface_(self, iid):
        if iid==axdebug.IID_IDebugExpressionContext:
            if self.expressionContext is None:
                self.expressionContext = _wrap(expressions.ExpressionContext(self.frame), axdebug.IID_IDebugExpressionContext)
            return self.expressionContext
#               from win32com.util import IIDToInterfaceName
#               print "DebugStackFrame QI with %s (%s)" % (IIDToInterfaceName(iid), str(iid))
        return 0
    #
    # The following need implementation
    def GetThread(self):
        """ Returns the thread associated with this stack frame.

        Result must be a IDebugApplicationThread
        """
        RaiseNotImpl("GetThread")

    def GetCodeContext(self):
        offset = self.codeContainer.GetPositionOfLine(self.lineno)
        return self.codeContainer.GetCodeContextAtPosition(offset)
    #
    # The following are usefully implemented
    def GetDescriptionString(self, fLong):
        filename = self.frame.f_code.co_filename
        s = ""
        if 0: #fLong:
            s = s + filename
        if self.frame.f_code.co_name:
            s = s + self.frame.f_code.co_name
        else:
            s = s + "<lambda>"
        return s
    def GetLanguageString(self, fLong):
        if fLong:
            return "Python ActiveX Scripting Engine"
        else:
            return "Python"
    def GetDebugProperty(self):
        return _wrap(StackFrameDebugProperty(self.frame), axdebug.IID_IDebugProperty)

class DebugStackFrameSniffer:
    _public_methods_ = ["EnumStackFrames"]
    _com_interfaces_ = [axdebug.IID_IDebugStackFrameSniffer]
    def __init__(self, debugger):
        self.debugger = debugger
        trace("DebugStackFrameSniffer instantiated")
#       def __del__(self):
#               print "DSFS dieing"
    def EnumStackFrames(self):
        trace("DebugStackFrameSniffer.EnumStackFrames called")
        return _wrap(EnumDebugStackFrames(self.debugger), axdebug.IID_IEnumDebugStackFrames)

# A DebugProperty for a stack frame.
class StackFrameDebugProperty:
    _com_interfaces_ = [axdebug.IID_IDebugProperty]
    _public_methods_ = ['GetPropertyInfo', 'GetExtendedInfo', 'SetValueAsString',
                        'EnumMembers', 'GetParent'
    ]
    def __init__(self, frame):
        self.frame = frame

    def GetPropertyInfo(self, dwFieldSpec, nRadix):
        RaiseNotImpl("StackFrameDebugProperty::GetPropertyInfo")
    def GetExtendedInfo(self): ### Note - not in the framework.
        RaiseNotImpl("StackFrameDebugProperty::GetExtendedInfo")

    def SetValueAsString(self, value, radix):
        #
        RaiseNotImpl("DebugProperty::SetValueAsString")

    def EnumMembers(self, dwFieldSpec, nRadix, iid):
        print("EnumMembers", dwFieldSpec, nRadix, iid)
        from . import expressions
        return expressions.MakeEnumDebugProperty(self.frame.f_locals, dwFieldSpec, nRadix, iid, self.frame)

    def GetParent(self):
        # return IDebugProperty
        RaiseNotImpl("DebugProperty::GetParent")
